/* implementacao do client.c */
#include "lib/client.h"
#include "webserver.h"

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>

enum {
  CLIENT_READY = 0,
  CLIENT_READ_HEADER,
  CLIENT_GET,
  
  CLIENT_QUIT,
  CLIENT_MAX_STATE
};

const char * header_400 = "HTTP/1.0 400 Bad Food\r\n\r\n";
const size_t header_400_len = 25;

const char * header_404 = "HTTP/1.0 404 Cannot find %s here.\r\n\r\nNot Found;";
const size_t header_404_len = 33+11;

const char * header_200 = "HTTP/1.0 200 Got it!\r\n"
                          "Connection: close\r\n"
                          "\r\n";
const size_t header_200_len = 22+19+2;

const char * header_500 = "HTTP/1.0 500 Bad Server :/\r\n\r\n";
const size_t header_500_len = 30;

typedef struct clientdata_t
{
  int state;
  char * filename;
  FILE * resource;
} clientdata;

/* preenche um buffer usando um arquivo
 * retorna quantos bytes conseguiu encher
 * ou negativo em caso de erro */
int fill(void * to, FILE * from, size_t max);

/* update dos clientes, substitui o update default
 * do servidor. */
int server_lite_update(client * c)
{
  
  char *in = 0, *out = 0; /* buffers */
  char *s = 0, *method = 0, *filename = 0, *path = 0; /* manipuladores de string */
  clientdata * data;
  int len;
  
  if (c->data == 0)
    c->data = calloc(1, sizeof(clientdata));
  
  data = (clientdata *)c->data;
  in = (char *)c->input;
  out = (char *)c->output;
  
  if (data->state == CLIENT_QUIT)
  return CLIENT_QUIT;
  
  if (c->inputlen != 0)
  {
    switch(data->state)
    {
      case CLIENT_READY: /* novo cliente, dados novos, pega header dele! */
        data->state = CLIENT_READ_HEADER;
        break;
        
      case CLIENT_GET:
        if (c->sent == c->outputlen) /* enviou tudo? */
        {
          /* reseta os contadores */
          c->outputlen = 0;
          c->sent = 0;
//           printf("reset! ");
        }
        
        len = fill(out + c->outputlen, data->resource, c->outputsz - c->outputlen);
        c->outputlen += len;
        if (c->outputlen == 0)
          data->state = CLIENT_QUIT;
        return 0;
        break;
        
      default:
        break;
    }
  }
  
  if (data->state == CLIENT_READ_HEADER)
  {
    if ((c->inputlen == c->inputsz)) /* ja leu todo o buffer e nada */
      goto badfood;
    
    if ((s = strstr(in, "\r\n\r\n")) == 0)
    {
      return 0; /* espere a proxima leitura */
    }
    else
    {
      printf("%s\n", in);
      /* marca a primeira linha */
      *strstr(in, "\r\n") = 0;
      s = in;
      method = strsep(&s, " ");
      
      
      /* TODO: suportar outros methods */
      if (strcmp(method, "GET") != 0) /* outros methods aqui no futuro */
        goto badfood;

      filename = strsep(&s, " ");
      if (!filename)
        goto badfood;
      
      path = calloc(1, strlen(serverpath) + strlen(filename) + 2);
      
      if (filename[0] != '/')
        sprintf(path, "%s/%s", serverpath, filename);
      else
        sprintf(path, "%s%s", serverpath, filename);
      
      data->filename = realpath(path, NULL);
      if (data->filename == 0)
        goto four0four;
      
      if (strstr(data->filename, serverpath) != data->filename)
        goto four0four;
      
      c->outputlen = header_200_len;
      strcpy(out, header_200);
      
      data->resource = fopen(data->filename, "r");
      if (data->resource == 0)
        goto badserver;
      
//       c->outputlen = fill(out+c->outputlen, data->serving, c->outputsz - c->outputlen);
      data->state = CLIENT_GET;
      
      /* na proxima iteracao eu jah sei que os headers foram enviados */
      free(path);
      return 0; 
    }
    
    four0four:
    {
      c->outputlen = sprintf(out, header_404, filename) +1;
      printf("%s\n", out);
      data->state = CLIENT_QUIT;
      if (path)
        free(path);
      return 0;
    }
      
    badfood:
    {
      strcpy(out, header_400);
      printf("%s\n", out);
      c->outputlen = header_400_len;
      if (path)
        free(path);
      data->state = CLIENT_QUIT;
      return 0;
    }
    
    badserver:
    {
      strcpy(out, header_500);
      printf("%s\n", out);
      c->outputlen = header_500_len;
      data->state = CLIENT_QUIT;
      return 0;
    }
  }
  return 0;
}

int server_lite_quit(client * clientp)
{
  clientdata * data = (clientdata*) clientp->data;
  if (data != 0)
  {
    if (data->filename)
      free(data->filename);
    
    if (data->resource)
      fclose(data->resource);
    
    free(clientp->data);
  }
  return 0;
}

int fill(void * to, FILE * from, size_t max) {
  int count = 0, total = 0;
  
  assert(from != NULL);
  assert(to != NULL);

  if (max == 0) return 0;
  
//   usleep(1000*100);
  count = fread(to, sizeof(char), max - total, from);
    
  return count;
}